Reactでスクリプトタグを含んだHTMLを挿入する(SSG/SSR対応)

您所在的位置:网站首页 react indexhtml Reactでスクリプトタグを含んだHTMLを挿入する(SSG/SSR対応)

Reactでスクリプトタグを含んだHTMLを挿入する(SSG/SSR対応)

2024-01-19 07:08| 来源: 网络整理| 查看: 265

いわゆるJAMstackを用いたWebメディアなどでは、コンテンツの構成が記事ごとに少しずつ違うけれどもマークダウンでは表現力が制限されるため、本文などコンテンツの一部をHTMLで記述して表示したいという状況はそれなりにあると思います。 そのような状況で、外部サービスから提供されるスクリプトタグを含んだコンテンツを表示したい、という要件に対応したものです。

dangerouslySetInnerHTMLの制限

ReactにはdangerouslySetInnerHTMLを用いることで、任意のDOM要素の子要素としてHTMLを挿入することができます。 ただし、この機能は内部的にinnerHTML用いています。innerHTMLはXSS攻撃に対する対策としてタグを実行しません。(ただし、インベントハンドラとしてスクリプトを実行することは可能です) そのため、スクリプトタグを含むHTMLはdangerouslySetInnerHTMLで設定することはできますが、そのスクリプトは実行されないということになります。

appendChildとcreateContextualFragmentの利用

WebAPIのappendChildメソッドは特定のノードに対して子ノードを追加するメソッドです。 このメソッドによって追加されたノードは通常のノードと同様に実行されるため、追加されたスクリプトタグも実行されます。 文字列として渡されたHTMLから要素を生成する際は、createContextualFragmentメソッドを用います。これはHTMLタグを含む文字列から、指定された範囲の開始点を起点とするドキュメントフラグメントを生成するメソッドです。

Reactでの擬似的なコードは以下となります。

const HTMLComponent = ({ htmlString }) => { const divRef = useRef(); useLayoutEffect(() => { if (!divRef.current) { return; } const fragment = document .createRange() .createContextualFragment(htmlString); divRef.current.appendChild(fragment); }, [htmlString]); return ; };

ただし、上記のコードではいくつか対応できないパターンがあります。

HubSpotの埋め込みコードのような外部ファイルの読み込みとそれに依存するインラインスクリプトがある場合 hbspt.forms.create({ portalId: "xxxxxx", formId: "xxxxxx" });

appendChildによって追加すると、2つのスクリプトタグが同時に実行されるため、2番目のインラインスクリプトのhbsptがグローバル変数として存在しない状態で実行されることとなります。 この問題は、外部ファイルを読み込むスクリプトタグを先に追加・実行し、それが完了したのちに残りの要素を追加することで解決することが可能です。

const HTMLComponent = ({ htmlString }) => { const divRef = useRef(); useLayoutEffect(() => { if (!divRef.current) { return; } (async () => { const scriptStrings = htmlString.match( / { resolve(); }); document.head.appendChild(scriptElement); }); }, Promise.resolve()); const fragment = document .createRange() .createContextualFragment(updatedHtmlString); divRef.current.appendChild(fragment); })(); }, [htmlString]); return ; };

外部ファイルを読み込むスクリプトタグの場合、ヘッダに追加して元のHTMLタグから取り除きます。 もし、すでに同じソースを読み込むスクリプトタグが存在する場合、多重の読み込みを防止するために処理をスキップします。 スクリプトタグのロードが完了した後に残りのHTMLを挿入することで、外部ファイルに依存するインラインスクリプトを問題なく実行することができます。

SSG/SSRでコンテンツがレンダリングされない

useLayoutEffectはサーバサイドでは実行されないため、SSR/SSGを行った際にレンダリングされていない状態となります。 この問題に対しては、挿入する要素に対して初期値を設定することで対応します。

const HTMLComponent = ({ htmlString }) => { const divRef = useRef(); const initialHTMLString = useMemo(() => { const scriptStrings = htmlString.match( /


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3